@openpresentation/opf 0.17.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/browser.js +38 -0
- package/dist/{catalog-refs-C4K4y-vh.d.ts → catalog-refs-BSfGELpt.d.ts} +1 -1
- package/dist/{catalog-schemas-g7RFQBdc.d.ts → catalog-schemas-CinPh5WA.d.ts} +1 -1
- package/dist/catalog.d.ts +2 -2
- package/dist/{chart-data-DTxdRRT1.d.ts → chart-data-BnNqgnC-.d.ts} +1 -1
- package/dist/{chunk-O2CX5NEH.js → chunk-64YSJZDH.js} +6 -5
- package/dist/{chunk-RA64UQKI.js → chunk-6WW2EDUW.js} +95 -71
- package/dist/{chunk-IS5H5JKH.js → chunk-CJPDTLGD.js} +1 -1
- package/dist/{chunk-BIC3SWPK.js → chunk-DBYZI75U.js} +30 -5
- package/dist/{chunk-NLNYNBTS.js → chunk-JT3QXA22.js} +25 -24
- package/dist/{chunk-YT7FMQER.js → chunk-JXVS7BJK.js} +1 -1
- package/dist/{chunk-IPW5BS3X.js → chunk-KXBZ6ZSB.js} +190 -172
- package/dist/{chunk-CW7GEAV3.js → chunk-MBU42S5Y.js} +291 -133
- package/dist/{chunk-TCD2FB2K.js → chunk-NS3VYB57.js} +5 -5
- package/dist/{chunk-WWYWFVOI.js → chunk-QLFWWHRF.js} +151 -25
- package/dist/{chunk-PPROJP2E.js → chunk-RAKEVM52.js} +1 -1
- package/dist/{chunk-ESRYBYLY.js → chunk-VCGRMNY2.js} +1 -1
- package/dist/{chunk-M4UR3NPN.js → chunk-YWXB4XAT.js} +2 -2
- package/dist/{chunk-O3CVNJTL.js → chunk-YZXTXX2D.js} +1 -1
- package/dist/{chunk-O263APNI.js → chunk-ZCFSDSUX.js} +3 -3
- package/dist/{chunk-N4EVOFEA.js → chunk-ZOHFLP3O.js} +4 -4
- package/dist/{composition-BbZ_9C-c.d.ts → composition-C-OoqvAr.d.ts} +80 -40
- package/dist/composition.d.ts +3 -3
- package/dist/composition.js +1 -1
- package/dist/{conversion-SBpTOwdg.d.ts → conversion-C5foedh6.d.ts} +96 -38
- package/dist/conversion-KGDVJM75.js +21 -0
- package/dist/convert.d.ts +4 -4
- package/dist/convert.js +7 -7
- package/dist/data.d.ts +7 -7
- package/dist/data.js +2 -2
- package/dist/{deck-report-Bx3naFlF.d.ts → deck-report-wszX7uo8.d.ts} +5 -5
- package/dist/deck.d.ts +8 -8
- package/dist/deck.js +10 -10
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +5 -5
- package/dist/docs.js +28 -28
- package/dist/examples.d.ts +1 -1
- package/dist/examples.js +181 -214
- package/dist/{finding-CDQ_wH8j.d.ts → finding-CT_f_vbD.d.ts} +1 -1
- package/dist/format.js +2 -2
- package/dist/index.d.ts +153 -20
- package/dist/index.js +62 -15
- package/dist/markdown.d.ts +12 -8
- package/dist/markdown.js +7 -7
- package/dist/node-engine.d.ts +37 -17
- package/dist/node-engine.js +39 -16
- package/dist/{pagination-BhX4M48Y.d.ts → pagination-BaUjDrTz.d.ts} +7 -4
- package/dist/pagination.d.ts +4 -4
- package/dist/pagination.js +4 -4
- package/dist/patch.d.ts +1 -1
- package/dist/patch.js +4 -4
- package/dist/{presentation-fb8CZmRq.d.ts → presentation-HondgJ3l.d.ts} +269 -382
- package/dist/repo-readme.js +1 -1
- package/dist/schemas.d.ts +4 -2
- package/dist/schemas.js +1 -1
- package/dist/spec/schemas/opf.schema.json +65 -70
- package/dist/types.d.ts +3 -3
- package/dist/{validation-types-iZbXfES6.d.ts → validation-types-PbGDrNUr.d.ts} +12 -7
- package/dist/validator.d.ts +8 -8
- package/dist/validator.js +3 -3
- package/dist/yaml.d.ts +6 -6
- package/dist/yaml.js +7 -7
- package/package.json +12 -10
- package/dist/node.d.ts +0 -60
- package/dist/node.js +0 -54
package/README.md
CHANGED
|
@@ -49,10 +49,10 @@ console.log(Object.keys(saved.catalogs.default.themes)); // ["classic"]
|
|
|
49
49
|
console.log(Object.keys(defaultCatalog.tones));
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
In Node,
|
|
52
|
+
In Node (and Bun and Deno), the root adds files: `convert` turns one file into another (a deck to PDF, PNG, SVG or PPTX, a PowerPoint file to a deck, one deck form to another, or bytes with no output path), `open` reads a deck file or imports a `.pptx`, and `save` writes one. The same import works in every runtime, so one namespace import covers an application:
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
|
-
import * as opf from "@openpresentation/opf
|
|
55
|
+
import * as opf from "@openpresentation/opf";
|
|
56
56
|
|
|
57
57
|
await opf.convert("deck.opf.md", "deck.pdf");
|
|
58
58
|
const deck = await opf.open("deck.opf.md");
|
|
@@ -61,7 +61,7 @@ await opf.save(deck, "deck.opf.md");
|
|
|
61
61
|
const { files } = await opf.convert(deck, { format: "pptx" }); // bytes, nothing written
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Drawing and PowerPoint go through the optional peers `@openpresentation/opf-render` and `@openpresentation/opf-pptx`, which
|
|
64
|
+
Drawing and PowerPoint go through the optional peers `@openpresentation/opf-render` and `@openpresentation/opf-pptx`, which core loads the first time a call needs them; npm does not install them for you. The root has conditional exports (OPF 0.18 removed `/node`): `node`, `bun` and `deno` get this full build, and `browser`, `worker`, `workerd` and `default` get a browser-safe build with the same names and the same types, where `open`, `save` and `convert` reject with `OPFApiError` code `node-only`. Every other subpath runs in a browser. `parseSlideSelection(3 | "1-3" | [1, 3], total)` reads slide selections in both. See [OPF files in Node](../../docs/node.md).
|
|
65
65
|
|
|
66
66
|
Use focused imports when you only need one surface:
|
|
67
67
|
|
|
@@ -91,7 +91,7 @@ The layout engine's names are not on the root. `composeSlide`, `fitText`, `layou
|
|
|
91
91
|
|
|
92
92
|
### Reading and writing a deck in any form
|
|
93
93
|
|
|
94
|
-
`parse(text, { format, filename, catalogs })` and `stringify(presentation, { format | filename })` (the root and `@openpresentation/opf/deck`) read and write a deck as JSON (canonical), YAML or Markdown. The format is the `format` option, else the file name (`.opf.md` is Markdown, `.yaml` and `.yml` are YAML, anything else JSON; a plain `.md` is not a deck), and never the content. `parse` returns the presentation and throws `OPFValidationError` for a syntax, schema or reference error; the error's `report.findings` are located by line and column in the text (`opf/json-syntax`, `yaml/<rule>` or `markdown/<rule>` for syntax errors). Warnings do not throw: `validate(text)` returns the full report. Both run in a browser; files are
|
|
94
|
+
`parse(text, { format, filename, catalogs })` and `stringify(presentation, { format | filename })` (the root and `@openpresentation/opf/deck`) read and write a deck as JSON (canonical), YAML or Markdown. The format is the `format` option, else the file name (`.opf.md` is Markdown, `.yaml` and `.yml` are YAML, anything else JSON; a plain `.md` is not a deck), and never the content. `parse` returns the presentation and throws `OPFValidationError` for a syntax, schema or reference error; the error's `report.findings` are located by line and column in the text (`opf/json-syntax`, `yaml/<rule>` or `markdown/<rule>` for syntax errors). Warnings do not throw: `validate(text)` returns the full report. Both run in a browser; files are the Node build's job (above).
|
|
95
95
|
|
|
96
96
|
### OPF as YAML
|
|
97
97
|
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { OPFApiError } from './chunk-QLFWWHRF.js';
|
|
2
|
+
export { OPFApiError, OPFExportError, OPFImportError, OPFMoveToCustomError, copySlides, edit, embed, fill, fillRecords, moveToCustom, parseSlideSelection, sameRecord, stats, updateFromCatalog } from './chunk-QLFWWHRF.js';
|
|
3
|
+
export { specFileEntries, specFileKinds, specFilePaths } from './chunk-HXHOZ7AH.js';
|
|
4
|
+
export { OPFDataImportError, ingest, parseTabularData } from './chunk-ZCFSDSUX.js';
|
|
5
|
+
export { DECK_FORMATS, deckFormatOf, parse, stringify } from './chunk-ZOHFLP3O.js';
|
|
6
|
+
export { fromMarkdown, toMarkdown } from './chunk-DBYZI75U.js';
|
|
7
|
+
import './chunk-NS3VYB57.js';
|
|
8
|
+
export { OPFMarkdownError } from './chunk-CJPDTLGD.js';
|
|
9
|
+
export { diff, merge } from './chunk-YWXB4XAT.js';
|
|
10
|
+
export { OPFPatchError, OPFPatchValidationError, applyPatch } from './chunk-YZXTXX2D.js';
|
|
11
|
+
export { OPFFormatError, format } from './chunk-JXVS7BJK.js';
|
|
12
|
+
export { OPFPaginationError, paginate, paginateSlide } from './chunk-64YSJZDH.js';
|
|
13
|
+
import './chunk-VCGRMNY2.js';
|
|
14
|
+
import './chunk-RAKEVM52.js';
|
|
15
|
+
export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, resolveSlideContext, validate, validateCatalogRecord, validationCategories, validationRules } from './chunk-JT3QXA22.js';
|
|
16
|
+
export { CHART_TYPES, DEFAULT_VARIABLE_DATE_FORMAT, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, LANGUAGES, LOGO_SHAPES, OPFVariableError, SLIDE_SCOPED_BUILTINS, SOCIAL_PLATFORMS, VARIABLE_KINDS, chartNumber, coerceVariableValue, datasetDiagnostics, formatDataNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, inlineChartData, inlineDatasets, inlineTableData, isDatasetRef, isTemplate, isXYChartType, listBuiltinVariables, listVariables, normalizeLanguageFamily, numberFormatError, resolveChartData, resolveDesignRecords, resolveFontScheme, resolveSlideVariables, resolveTableData, resolveVariables, suggestChartNumberFix, tableCellDisplayValue, toExcelNumberFormat, unusedDatasets, variableDeclarations } from './chunk-MBU42S5Y.js';
|
|
17
|
+
export { catalogDisplayKinds, catalogSchemaNames } from './chunk-TRDXRRQK.js';
|
|
18
|
+
export { CATALOG_GROUP_PATTERN, CATALOG_REFERENCE_PATTERN, OPFCatalogsOptionError, OPFUnresolvedReferenceError, catalogGroupSource, catalogKinds, catalogRecords, catalogReferenceSites, parseReference, resolveReference, unresolvedReference } from './chunk-SPFV27O5.js';
|
|
19
|
+
export { audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './chunk-6WW2EDUW.js';
|
|
20
|
+
export { FONT_POLICY, applyFontPolicyDecisions, fontAvailabilityDiagnostics, fontPolicyFor } from './chunk-WXSPETQ6.js';
|
|
21
|
+
|
|
22
|
+
// src/browser.ts
|
|
23
|
+
var nodeOnly = (name, message) => new OPFApiError(`\`${name}\` ${message}`, "node-only", { details: { function: name, build: "browser" } });
|
|
24
|
+
var PARSE = "In a browser or a worker, read the text yourself and call parse(text, { filename }).";
|
|
25
|
+
var STRINGIFY = "In a browser or a worker, call stringify(deck, { filename }) and store or download the text.";
|
|
26
|
+
var CONVERT = "In a browser or a worker, change deck forms with parse and stringify (YAML, Markdown, JSON), and draw slides with @openpresentation/opf-render's /export-browser entry.";
|
|
27
|
+
var open = async () => {
|
|
28
|
+
throw nodeOnly("open", `reads files and needs Node. ${PARSE}`);
|
|
29
|
+
};
|
|
30
|
+
var save = async () => {
|
|
31
|
+
throw nodeOnly("save", `writes files and needs Node. ${STRINGIFY}`);
|
|
32
|
+
};
|
|
33
|
+
var convert = (async (_input, output) => {
|
|
34
|
+
const inMemory = output !== null && typeof output === "object";
|
|
35
|
+
throw nodeOnly("convert", inMemory ? `needs Node in OPF 0.18, also for the in-memory convert(deck, { format }). ${CONVERT}` : `reads and writes files and needs Node. ${CONVERT}`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export { convert, open, save };
|
|
@@ -125,4 +125,4 @@ declare function catalogReferenceSites(document: unknown, options?: {
|
|
|
125
125
|
/** Records a picker can offer for `kind`: the document's embedded records, then those of the registered catalogs its groups name, with the reference to write for each. */
|
|
126
126
|
declare function catalogRecords(document: unknown, kind: CatalogKind, options?: CatalogOptions): ResolvedReference[];
|
|
127
127
|
|
|
128
|
-
export {
|
|
128
|
+
export { type CatalogKind as C, OPFCatalogsOptionError as O, type RecordProvenance as R, type UnresolvedReferenceDiagnostic as U, type Catalog as a, type CatalogOptions as b, CATALOG_GROUP_PATTERN as c, CATALOG_REFERENCE_PATTERN as d, type CatalogRecordMap as e, type CatalogRecords as f, type CatalogReferenceSite as g, OPFUnresolvedReferenceError as h, type ResolvedReference as i, catalogGroupSource as j, catalogKinds as k, catalogRecords as l, catalogReferenceSites as m, parseReference as p, resolveReference as r, unresolvedReference as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as CatalogKind } from './catalog-refs-BSfGELpt.js';
|
|
2
2
|
import { SchemaName } from './schemas.js';
|
|
3
3
|
|
|
4
4
|
/** Kinds of catalog display metadata: published by catalogs for pickers, never referenced by documents. */
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { C as CatalogDisplayKind } from './catalog-schemas-
|
|
1
|
+
import { a as Catalog } from './catalog-refs-BSfGELpt.js';
|
|
2
|
+
import { C as CatalogDisplayKind } from './catalog-schemas-CinPh5WA.js';
|
|
3
3
|
import './schemas.js';
|
|
4
4
|
import './json-BFSdESOE.js';
|
|
5
5
|
|
|
@@ -185,4 +185,4 @@ declare function resolveTableData(table: unknown, document?: unknown, options?:
|
|
|
185
185
|
/** Display value of a table cell: a number with a format (cell, else column) becomes formatted text; anything else is returned unchanged. */
|
|
186
186
|
declare function tableCellDisplayValue(cell: DataTableCell, columnFormat?: string): DataTableCell;
|
|
187
187
|
|
|
188
|
-
export {
|
|
188
|
+
export { resolveChartData as A, resolveTableData as B, type ChartMapping as C, type DataCellValue as D, suggestChartNumberFix as E, tableCellDisplayValue as F, toExcelNumberFormat as G, unusedDatasets as H, type ChartComboSeries as I, type ResolvedChartData as R, type ChartNumberFix as a, type ChartNumberFixOperation as b, type ChartNumberFixOptions as c, type DataColumn as d, type DataDiagnostic as e, type DataDiagnosticCode as f, type DataResolveOptions as g, type DataSourceRef as h, type DataStyledCell as i, type DataTableCell as j, type DataTableHeader as k, type DataTableValue as l, type DataTextRun as m, type Dataset as n, type DatasetRef as o, type ResolvedTableData as p, chartNumber as q, datasetDiagnostics as r, formatDataNumber as s, fromExcelNumberFormat as t, inlineChartData as u, inlineDatasets as v, inlineTableData as w, isDatasetRef as x, isXYChartType as y, numberFormatError as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { assertValid, resolveSlideScriptMeasurement, resolveSlideContext } from './chunk-
|
|
2
|
-
import { resolveSlideVariables, usesSlideBuiltin, composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, slideTokenSpans, sliceNumberedItems } from './chunk-
|
|
1
|
+
import { assertValid, resolveSlideScriptMeasurement, resolveSlideContext } from './chunk-JT3QXA22.js';
|
|
2
|
+
import { resolveSlideVariables, usesSlideBuiltin, composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, slideTokenSpans, sliceNumberedItems } from './chunk-MBU42S5Y.js';
|
|
3
3
|
import { checkCatalogsOption } from './chunk-SPFV27O5.js';
|
|
4
4
|
|
|
5
5
|
// src/pagination.ts
|
|
@@ -122,10 +122,11 @@ function paginateSlide(input, options = {}) {
|
|
|
122
122
|
if (options.layout !== void 0) composeOptions.layout = readableLayout(options.layout);
|
|
123
123
|
const presentationSlides = options.presentation?.slides;
|
|
124
124
|
const slideCount = options.slideCount ?? (Array.isArray(presentationSlides) && presentationSlides.length ? presentationSlides.length : void 0);
|
|
125
|
+
const logoContext = options.presentation !== void 0 ? { presentation: options.presentation, darkBackground: options.darkBackground === true } : {};
|
|
125
126
|
const geometry = (slide, pageIndex = 0) => {
|
|
126
127
|
if (++evaluations > 2e4) throw new OPFPaginationError("Pagination exceeded its layout evaluation limit. Split the input into smaller sections.");
|
|
127
128
|
const slideNumber = (options.slideNumber ?? sourceIndex + 1) + pageIndex;
|
|
128
|
-
return composeSlide(resolveSlideVariables(withReadability(slide, true), { slideNumber, ...slideCount !== void 0 ? { slideCount } : {} }), { ...composeOptions, slideNumber });
|
|
129
|
+
return composeSlide(resolveSlideVariables(withReadability(slide, true), { slideNumber, ...slideCount !== void 0 ? { slideCount } : {}, ...logoContext }), { ...composeOptions, slideNumber });
|
|
129
130
|
};
|
|
130
131
|
const initial = geometry(source);
|
|
131
132
|
const fit = (diagnostics) => diagnostics.filter((issue) => issue.code === "text-overflow" || issue.code === "small-cell" || !!initial.furniture?.diagnostics.includes(issue));
|
|
@@ -141,7 +142,7 @@ function paginateSlide(input, options = {}) {
|
|
|
141
142
|
if (!fit(initial.diagnostics).length) return { slides: [source], pages: [{ slideIndex: sourceIndex, mappings: initial.items.map((item) => ({ sourcePath: item.path, outputPath: item.path })), ...initial.furniture ? { repeatedMappings: repeatedMappings(0) } : {} }] };
|
|
142
143
|
const headerIssues = initial.diagnostics.filter((issue) => headingFields.has(issue.path.slice(sourceBase.length + 1)) || initial.furniture?.diagnostics.includes(issue));
|
|
143
144
|
if (headerIssues.length) throw new OPFPaginationError("Repeated headings or header/footer content cannot fit or resolve. Change the repeated content or slide design before pagination.", headerIssues);
|
|
144
|
-
const tokens = resolveSlideVariables(source, { slideNumber: 1, slideCount: 1 }) !== source;
|
|
145
|
+
const tokens = resolveSlideVariables(source, { slideNumber: 1, slideCount: 1, ...logoContext }) !== source;
|
|
145
146
|
const sourceValue = (path, fallback) => {
|
|
146
147
|
if (!tokens || !path.startsWith(`${sourceBase}.`)) return fallback;
|
|
147
148
|
let node = source;
|
|
@@ -312,7 +313,7 @@ function paginate(input, options = {}) {
|
|
|
312
313
|
const { output, pages } = result;
|
|
313
314
|
presentation.slides = output;
|
|
314
315
|
assertValid(presentation, { only: ["format"] });
|
|
315
|
-
return { presentation, pages };
|
|
316
|
+
return { presentation, pages, layout: options.fonts ? "measured" : "estimated" };
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
export { OPFPaginationError, paginate, paginateSlide };
|
|
@@ -52,7 +52,7 @@ var presentation = {
|
|
|
52
52
|
"minItems": 1
|
|
53
53
|
}
|
|
54
54
|
],
|
|
55
|
-
"description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) supplies the
|
|
55
|
+
"description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) supplies the cover and section logo, the 'socials' header/footer field and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
|
|
56
56
|
},
|
|
57
57
|
"speaker": {
|
|
58
58
|
"oneOf": [
|
|
@@ -775,7 +775,7 @@ var presentation = {
|
|
|
775
775
|
},
|
|
776
776
|
"Organization": {
|
|
777
777
|
"type": "object",
|
|
778
|
-
"description": "An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the
|
|
778
|
+
"description": "An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the cover and section logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'organization.<id>.<field>'), and every logo a slide-scoped image ('var:organization.logo.icon', 'var:organization.<id>.logo'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
|
|
779
779
|
"required": [
|
|
780
780
|
"id",
|
|
781
781
|
"name"
|
|
@@ -809,12 +809,29 @@ var presentation = {
|
|
|
809
809
|
]
|
|
810
810
|
},
|
|
811
811
|
"logo": {
|
|
812
|
-
"
|
|
813
|
-
|
|
812
|
+
"oneOf": [
|
|
813
|
+
{
|
|
814
|
+
"$ref": "#/$defs/Asset"
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
"$ref": "#/$defs/OrganizationLogo"
|
|
818
|
+
}
|
|
819
|
+
],
|
|
820
|
+
"description": "The organization's logo. A path, URL or Asset is one logo for every shape; an object gives up to four shapes (full, stacked, icon, wordmark), each a path or Asset, or { onLight, onDark } for the two backgrounds. A path is resolved against the OPF file location; an HTTPS URL, data URI or 'asset:<id>' reference works too. SVG (preferred for vector logos) or PNG with transparency are typical. The primary organization's full logo (role 'primary', else the first organization) is drawn on cover and section slides unless design.logo names another or is false. Every logo is also a slide-scoped built-in image, placed with a whole-field reference in any image field: 'var:organization.logo' (the full logo), 'var:organization.logo.icon' (likewise stacked, wordmark, full), and 'var:organization.<id>.logo' or 'var:organization.<id>.logo.<shape>' for another organization. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon. The onLight or onDark value is chosen for each slide's background; a missing one uses the other. See docs/templates-and-variables.md and docs/design-resolution.md.",
|
|
814
821
|
"examples": [
|
|
822
|
+
"./assets/acme-logo.svg",
|
|
823
|
+
{
|
|
824
|
+
"full": "./assets/acme-logo.svg",
|
|
825
|
+
"icon": "./assets/acme-mark.svg"
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
"full": {
|
|
829
|
+
"onLight": "./assets/acme-logo.svg",
|
|
830
|
+
"onDark": "./assets/acme-logo-white.svg"
|
|
831
|
+
},
|
|
832
|
+
"icon": "./assets/acme-mark.svg"
|
|
833
|
+
},
|
|
815
834
|
"https://cdn.acme.com/logo.svg",
|
|
816
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9V3iWggAAAAASUVORK5CYII=",
|
|
817
|
-
"./assets/logo.jpg",
|
|
818
835
|
"asset:acme-logo"
|
|
819
836
|
]
|
|
820
837
|
},
|
|
@@ -863,7 +880,7 @@ var presentation = {
|
|
|
863
880
|
"sponsor",
|
|
864
881
|
"host"
|
|
865
882
|
],
|
|
866
|
-
"description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (
|
|
883
|
+
"description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (cover and section logo, the 'socials' header/footer field, and the 'organization.<field>' built-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
|
|
867
884
|
},
|
|
868
885
|
"socials": {
|
|
869
886
|
"$ref": "#/$defs/Socials",
|
|
@@ -1086,20 +1103,19 @@ var presentation = {
|
|
|
1086
1103
|
"logo": {
|
|
1087
1104
|
"oneOf": [
|
|
1088
1105
|
{
|
|
1089
|
-
"
|
|
1106
|
+
"type": "string",
|
|
1107
|
+
"pattern": "^var:organization(\\.[A-Za-z0-9_-]+)?\\.logo(\\.(full|stacked|icon|wordmark))?$"
|
|
1090
1108
|
},
|
|
1091
1109
|
{
|
|
1092
|
-
"
|
|
1110
|
+
"type": "boolean",
|
|
1111
|
+
"const": false
|
|
1093
1112
|
}
|
|
1094
1113
|
],
|
|
1095
|
-
"description": "
|
|
1114
|
+
"description": "Which logo covers, section dividers and picture bullets draw. Logos live on the organization (Organization.logo); without this field covers and sections draw the primary organization's full logo and picture bullets its icon. A logo reference overrides that: 'var:organization.beta.logo' draws organization beta's logo, and a reference that names a shape ('var:organization.logo.wordmark') draws that shape everywhere this field applies. false draws no logo. A slide's design.logo overrides the deck's; absence inherits. An override naming an organization without a logo draws nothing (validation warns). Placement (reference engines, vetoable): on a cover or section slide (no body payload on a heading-only layout such as title, title-subtitle or section-divider, or no layout) the logo is drawn at the top-left of the free area, inside the slide padding and below any header furniture, in a box 56 reference pixels tall and at most four times as wide; the tag/title/subtitle group then centers in the remaining span. Content slides never get an automatic logo; a header or footer zone places one with image: 'var:organization.logo.icon'. composeSlide returns the cover box as geometry.logo.",
|
|
1096
1115
|
"examples": [
|
|
1097
|
-
"
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
"light": "asset:acme-logo-white",
|
|
1101
|
-
"icon": "asset:acme-icon"
|
|
1102
|
-
}
|
|
1116
|
+
"var:organization.logo.wordmark",
|
|
1117
|
+
"var:organization.beta.logo",
|
|
1118
|
+
false
|
|
1103
1119
|
]
|
|
1104
1120
|
},
|
|
1105
1121
|
"watermark": {
|
|
@@ -1147,7 +1163,7 @@ var presentation = {
|
|
|
1147
1163
|
"text": "{{slide.section}}"
|
|
1148
1164
|
},
|
|
1149
1165
|
"right": {
|
|
1150
|
-
"image": "
|
|
1166
|
+
"image": "var:organization.logo.icon"
|
|
1151
1167
|
}
|
|
1152
1168
|
}
|
|
1153
1169
|
]
|
|
@@ -1167,7 +1183,7 @@ var presentation = {
|
|
|
1167
1183
|
false,
|
|
1168
1184
|
{
|
|
1169
1185
|
"left": {
|
|
1170
|
-
"image": "
|
|
1186
|
+
"image": "var:organization.logo.icon"
|
|
1171
1187
|
},
|
|
1172
1188
|
"center": {
|
|
1173
1189
|
"text": "Confidential"
|
|
@@ -1234,7 +1250,7 @@ var presentation = {
|
|
|
1234
1250
|
"character",
|
|
1235
1251
|
"image"
|
|
1236
1252
|
],
|
|
1237
|
-
"description": "Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the layout record's design.listBullet, then 'character'. 'character' (the default) draws the glyph marker. 'image' draws the
|
|
1253
|
+
"description": "Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the layout record's design.listBullet, then 'character'. 'character' (the default) draws the glyph marker. 'image' draws the primary organization's icon logo (or the organization and shape design.logo names; the onDark value on dark backgrounds) as a picture bullet: a square of the marker's font size whose bottom sits on the marker baseline, so list geometry does not change. Without a logo the glyph is drawn and the engine reports unresolved-content at this field."
|
|
1238
1254
|
}
|
|
1239
1255
|
}
|
|
1240
1256
|
},
|
|
@@ -1666,7 +1682,7 @@ var presentation = {
|
|
|
1666
1682
|
"additionalProperties": {
|
|
1667
1683
|
"$ref": "#/$defs/Variable"
|
|
1668
1684
|
},
|
|
1669
|
-
"description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone
|
|
1685
|
+
"description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. Organization logos are slide-scoped whole-field images: 'var:organization.logo', 'var:organization.logo.<shape>' (full, stacked, icon, wordmark), 'var:organization.<id>.logo' and 'var:organization.<id>.logo.<shape>' resolve per slide to the onLight or onDark asset for its background. See docs/templates-and-variables.md.",
|
|
1670
1686
|
"examples": [
|
|
1671
1687
|
{
|
|
1672
1688
|
"risk": "#B42318",
|
|
@@ -2509,60 +2525,71 @@ var presentation = {
|
|
|
2509
2525
|
}
|
|
2510
2526
|
}
|
|
2511
2527
|
},
|
|
2512
|
-
"
|
|
2528
|
+
"OrganizationLogo": {
|
|
2513
2529
|
"type": "object",
|
|
2514
|
-
"description": "
|
|
2530
|
+
"description": "An organization's logo in up to four shapes. Each shape is a path or Asset, or { onLight, onDark } for the two backgrounds. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon; within a shape a missing onLight or onDark uses the other. Shapes are placed with slide-scoped references such as 'var:organization.logo.icon'.",
|
|
2515
2531
|
"additionalProperties": false,
|
|
2532
|
+
"minProperties": 1,
|
|
2516
2533
|
"properties": {
|
|
2517
|
-
"
|
|
2518
|
-
"$ref": "#/$defs/
|
|
2519
|
-
"description": "
|
|
2520
|
-
},
|
|
2521
|
-
"light": {
|
|
2522
|
-
"$ref": "#/$defs/Asset",
|
|
2523
|
-
"description": "Light-colored full-lockup logo intended for rendering on dark backgrounds."
|
|
2524
|
-
},
|
|
2525
|
-
"dark": {
|
|
2526
|
-
"$ref": "#/$defs/Asset",
|
|
2527
|
-
"description": "Dark-colored full-lockup logo intended for rendering on light backgrounds."
|
|
2534
|
+
"full": {
|
|
2535
|
+
"$ref": "#/$defs/LogoSource",
|
|
2536
|
+
"description": "The full logo (lockup): mark and name side by side. Drawn on cover and section slides, and placed by 'var:organization.logo'."
|
|
2528
2537
|
},
|
|
2529
2538
|
"stacked": {
|
|
2530
|
-
"$ref": "#/$defs/
|
|
2531
|
-
"description": "
|
|
2532
|
-
},
|
|
2533
|
-
"stackedLight": {
|
|
2534
|
-
"$ref": "#/$defs/Asset",
|
|
2535
|
-
"description": "Light-colored stacked logo variant intended for rendering on dark backgrounds."
|
|
2536
|
-
},
|
|
2537
|
-
"stackedDark": {
|
|
2538
|
-
"$ref": "#/$defs/Asset",
|
|
2539
|
-
"description": "Dark-colored stacked logo variant intended for rendering on light backgrounds."
|
|
2539
|
+
"$ref": "#/$defs/LogoSource",
|
|
2540
|
+
"description": "The stacked lockup: mark above the name, for portrait or square slots. Placed by 'var:organization.logo.stacked'."
|
|
2540
2541
|
},
|
|
2541
2542
|
"icon": {
|
|
2542
|
-
"$ref": "#/$defs/
|
|
2543
|
-
"description": "
|
|
2544
|
-
},
|
|
2545
|
-
"iconLight": {
|
|
2546
|
-
"$ref": "#/$defs/Asset",
|
|
2547
|
-
"description": "Light-colored icon variant intended for rendering on dark backgrounds."
|
|
2548
|
-
},
|
|
2549
|
-
"iconDark": {
|
|
2550
|
-
"$ref": "#/$defs/Asset",
|
|
2551
|
-
"description": "Dark-colored icon variant intended for rendering on light backgrounds."
|
|
2543
|
+
"$ref": "#/$defs/LogoSource",
|
|
2544
|
+
"description": "The mark or symbol without the name, for tight spaces such as header and footer zones and picture bullets. Placed by 'var:organization.logo.icon'."
|
|
2552
2545
|
},
|
|
2553
2546
|
"wordmark": {
|
|
2554
|
-
"$ref": "#/$defs/
|
|
2555
|
-
"description": "
|
|
2547
|
+
"$ref": "#/$defs/LogoSource",
|
|
2548
|
+
"description": "The name set in the brand's lettering, without the mark. Placed by 'var:organization.logo.wordmark'."
|
|
2549
|
+
}
|
|
2550
|
+
},
|
|
2551
|
+
"examples": [
|
|
2552
|
+
{
|
|
2553
|
+
"full": "./assets/acme-logo.svg",
|
|
2554
|
+
"icon": "./assets/acme-mark.svg"
|
|
2556
2555
|
},
|
|
2557
|
-
|
|
2558
|
-
"
|
|
2559
|
-
|
|
2556
|
+
{
|
|
2557
|
+
"full": {
|
|
2558
|
+
"onLight": "./assets/acme-logo.svg",
|
|
2559
|
+
"onDark": "./assets/acme-logo-white.svg"
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
]
|
|
2563
|
+
},
|
|
2564
|
+
"LogoSource": {
|
|
2565
|
+
"description": "One logo shape: a path or Asset for every background, or { onLight, onDark }. onLight is drawn on light backgrounds (usually dark artwork) and onDark on dark backgrounds (usually light artwork), by each slide's background; a missing one uses the other.",
|
|
2566
|
+
"oneOf": [
|
|
2567
|
+
{
|
|
2568
|
+
"$ref": "#/$defs/Asset"
|
|
2560
2569
|
},
|
|
2561
|
-
|
|
2562
|
-
"
|
|
2563
|
-
"
|
|
2570
|
+
{
|
|
2571
|
+
"type": "object",
|
|
2572
|
+
"additionalProperties": false,
|
|
2573
|
+
"minProperties": 1,
|
|
2574
|
+
"properties": {
|
|
2575
|
+
"onLight": {
|
|
2576
|
+
"$ref": "#/$defs/Asset",
|
|
2577
|
+
"description": "The logo for light backgrounds, usually dark artwork."
|
|
2578
|
+
},
|
|
2579
|
+
"onDark": {
|
|
2580
|
+
"$ref": "#/$defs/Asset",
|
|
2581
|
+
"description": "The logo for dark backgrounds, usually light or white artwork."
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2564
2584
|
}
|
|
2565
|
-
|
|
2585
|
+
],
|
|
2586
|
+
"examples": [
|
|
2587
|
+
"./assets/acme-mark.svg",
|
|
2588
|
+
{
|
|
2589
|
+
"onLight": "./assets/acme-logo.svg",
|
|
2590
|
+
"onDark": "./assets/acme-logo-white.svg"
|
|
2591
|
+
}
|
|
2592
|
+
]
|
|
2566
2593
|
},
|
|
2567
2594
|
"Watermark": {
|
|
2568
2595
|
"type": "object",
|
|
@@ -2625,13 +2652,9 @@ var presentation = {
|
|
|
2625
2652
|
},
|
|
2626
2653
|
"HeaderFooterItem": {
|
|
2627
2654
|
"type": "object",
|
|
2628
|
-
"description": "One header/footer zone. Every configured field renders
|
|
2655
|
+
"description": "One header/footer zone. Every configured field renders, side by side in one row in the order image, text, socials, date (mirrored in a right-to-left deck), aligned to the zone's edge (left, center or right) and vertically centered on each other; a line break inside text or socials ('\\n') stacks lines within that part. When the parts are wider than the zone their text wraps, and a row that cannot fit even so is reported as overflow. Generated values such as the slide number, the slide count, the section, the organization or the speaker are variables inside text ('{{slide.number}} / {{deck.slideCount}}', '{{organization.name}}'), so one text writes several values in the author's order. An organization logo is image: 'var:organization.logo.icon'. See docs/templates-and-variables.md.",
|
|
2629
2656
|
"additionalProperties": false,
|
|
2630
2657
|
"properties": {
|
|
2631
|
-
"logo": {
|
|
2632
|
-
"type": "boolean",
|
|
2633
|
-
"description": "Whether to render the deck's icon logo in this zone: a slide's design.logo, then design.logo, then the primary organization's logo (LogoSet icon variants first, light ones on dark backgrounds). It is a generated image part in the same box as image; without a logo the engine reports unresolved-content at this field. Use image for a literal picture that is not the deck logo."
|
|
2634
|
-
},
|
|
2635
2658
|
"text": {
|
|
2636
2659
|
"type": "string",
|
|
2637
2660
|
"description": "Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-ins '{{slide.number}}', '{{slide.section}}' and '{{deck.slideCount}}' for each slide as it is laid out. Each '{{slide.number}}' is a live slide-number field: PPTX export writes it as a native field when its value fits within one accepted text line, and as static text with a diagnostic otherwise. '{{deck.slideCount}}' is fixed text, because PowerPoint has no slide-count field. Write '\\{{' for literal braces.",
|
|
@@ -2644,10 +2667,11 @@ var presentation = {
|
|
|
2644
2667
|
},
|
|
2645
2668
|
"image": {
|
|
2646
2669
|
"$ref": "#/$defs/Asset",
|
|
2647
|
-
"description": "
|
|
2670
|
+
"description": "Image rendered first in this zone's row, such as a logo, partner mark, certification badge, or icon: a path, URL or Asset, or an organization logo reference ('var:organization.logo.icon', 'var:organization.<id>.logo.wordmark'), which resolves for each slide's background (onLight or onDark).",
|
|
2648
2671
|
"examples": [
|
|
2649
|
-
"
|
|
2650
|
-
"./assets/footer-badge.svg"
|
|
2672
|
+
"var:organization.logo.icon",
|
|
2673
|
+
"./assets/footer-badge.svg",
|
|
2674
|
+
"var:organization.beta.logo"
|
|
2651
2675
|
]
|
|
2652
2676
|
},
|
|
2653
2677
|
"date": {
|
|
@@ -3087,7 +3111,7 @@ var presentation = {
|
|
|
3087
3111
|
},
|
|
3088
3112
|
"hidden": {
|
|
3089
3113
|
"type": "boolean",
|
|
3090
|
-
"description": "Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (opf CLI: --include-hidden; the deck-level
|
|
3114
|
+
"description": "Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (opf CLI: --include-hidden; the deck-level toSvg: the opt-in skipHidden). A slide named explicitly (opf convert --slides, toSvg with a slide number) is still rendered."
|
|
3091
3115
|
},
|
|
3092
3116
|
"composition": {
|
|
3093
3117
|
"$ref": "#/$defs/Composition"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Ctx, splitLines, lineRange, readYamlMapping, NOT_CHECKED, OPFMarkdownError, writeYaml, readCsv, DECIMAL, csvField, scalarText } from './chunk-
|
|
2
|
-
import { parseQuoteLines, levelsFromIndents, parseCodeFence, splitWhen, fenceCode } from './chunk-
|
|
3
|
-
import { same, isRecord, runText, isPlainRun, FORMATTING } from './chunk-
|
|
4
|
-
import { validate } from './chunk-
|
|
1
|
+
import { Ctx, splitLines, lineRange, readYamlMapping, NOT_CHECKED, OPFMarkdownError, writeYaml, readCsv, DECIMAL, csvField, scalarText } from './chunk-CJPDTLGD.js';
|
|
2
|
+
import { parseQuoteLines, levelsFromIndents, parseCodeFence, splitWhen, fenceCode } from './chunk-VCGRMNY2.js';
|
|
3
|
+
import { same, isRecord, runText, isPlainRun, FORMATTING } from './chunk-RAKEVM52.js';
|
|
4
|
+
import { validate } from './chunk-JT3QXA22.js';
|
|
5
5
|
import { checkCatalogsOption } from './chunk-SPFV27O5.js';
|
|
6
6
|
|
|
7
7
|
// src/markdown/canon.ts
|
|
@@ -789,6 +789,30 @@ function splitSegments(lines, split) {
|
|
|
789
789
|
}
|
|
790
790
|
return segments;
|
|
791
791
|
}
|
|
792
|
+
function isOutline(lines) {
|
|
793
|
+
let fence;
|
|
794
|
+
let comment = false;
|
|
795
|
+
let notes = false;
|
|
796
|
+
let headings = 0;
|
|
797
|
+
for (const { text } of lines) {
|
|
798
|
+
if (fence !== void 0) {
|
|
799
|
+
if (fenceClose(fence).test(text)) fence = void 0;
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
if (comment) {
|
|
803
|
+
if (text.includes("-->")) comment = false;
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (SEPARATOR.test(text)) return false;
|
|
807
|
+
if (notes) continue;
|
|
808
|
+
if (/^#(?:[ \t]|$)/.test(text)) headings++;
|
|
809
|
+
const open = fenceStart(text);
|
|
810
|
+
if (open) fence = open.fence;
|
|
811
|
+
else if (COMMENT.test(text) && !text.includes("-->", text.indexOf("<!--") + 4)) comment = true;
|
|
812
|
+
else if (NOTES.test(text)) notes = true;
|
|
813
|
+
}
|
|
814
|
+
return headings >= 2;
|
|
815
|
+
}
|
|
792
816
|
function joinParagraph(lines) {
|
|
793
817
|
let out = "";
|
|
794
818
|
lines.forEach((raw, k) => {
|
|
@@ -1776,7 +1800,8 @@ function fromMarkdown(markdown, options = {}) {
|
|
|
1776
1800
|
body = lines.slice(matter.end);
|
|
1777
1801
|
}
|
|
1778
1802
|
const document = { ...options.defaults ?? {}, ...front };
|
|
1779
|
-
const
|
|
1803
|
+
const split = options.split ?? "auto";
|
|
1804
|
+
const segments = splitSegments(body, split === "headings" || split === "auto" && isOutline(body) ? "headings" : "rules");
|
|
1780
1805
|
const slides = [];
|
|
1781
1806
|
segments.forEach((segment, index) => {
|
|
1782
1807
|
if (emptySegment(segment)) {
|